﻿using System;
using System.Collections.Generic;
using System.IO;

using Commons = NougakudoCompanion.Commons;

namespace NougakuDoController
{
    public class Nougakudo
    {
        string rubyExecutable;
        string rubyPath;
        string configFile;
        string configPath;
        bool debugging;
        bool warnings;
        int numProcess;
        string environment;
        bool virtualHost = false;   // Add for NougakuDo 1.1.0 above.
        string hostName = "";       // Add for NougakuDo 1.1.0 above.
        //bool ennou110 = false;      // Add for sub directory mode of NougakuDo 1.1.0 above. Delete to check NougakuDo 1.1.0 above.
        string name = "";           // Add for sub directory mode of NougakuDo 1.1.0 above.

        List<string> errors;

        /// <summary>
        /// Generate configuration file (configEnnou.ru) and command file (serverEnnou.txt)
        /// This is base on NougakuDo 1.0.4.
        /// </summary>
        /// <param name="rubyExecutable">Full path of ruby.exe in NougakuDo\bin folder</param>
        /// <param name="configFile">Full path of config.ru in your rails application</param>
        /// <param name="environment">production or development or test then default is production</param>
        /// <param name="numProcess">numbers of process. The max is 2 * number of logical processors. Default value = 2.</param>
        /// <param name="debugging">set debugging flags. default is false.</param>
        /// <param name="warnings">set warnings flags. default is false.</param>
        /// <param name="virtualHost">set virtual host. default is false. (Add for NougakuDo 1.1.0 above.)</param>
        /// <param name="ennou110">set true for NougakuDo 1.1.0 above. so setting ENV['RAILS_RELATIVE_URL_ROOT'] to environment.rb</param>
        public Nougakudo(string rubyExecutable, string configFile, string environment = "production",
                         int numProcess = 2, bool debugging = false, bool warnings = false,
                         bool virtualHost = false, string hostName = "localhost") //, bool ennou110 = false) // Delete to check NougakuDo 1.1.0 above.
        {
            this.rubyExecutable = rubyExecutable;
            this.rubyPath = Path.GetDirectoryName(rubyExecutable);

            this.configFile = configFile;
            this.configPath = Path.GetDirectoryName(configFile);
            this.name = Path.GetFileName(this.configPath);  // Add for sub directory mode.

            this.debugging = debugging;
            this.warnings = warnings;
            this.numProcess = numProcess;
            this.environment = environment;

            this.virtualHost = virtualHost; // Add for NougakuDo 1.1.0 above.
            this.hostName = hostName;
            //this.ennou110 = ennou110;       // Add for sub directory mode.  // Delete to check NougakuDo 1.1.0 above.

            this.errors = new List<string>();
        }

        /// <summary>
        /// Generate configuration file (configEnnou.ru) and command file (serverEnnou.txt)
        /// </summary>
        /// <returns></returns>
        public Tuple<bool, List<string>> Generate()
        {
            var ret = GenerateConfigurationFile();
            if (!ret) return new Tuple<bool, List<string>>(false, errors);
            ret = GenerateCommandFile();

            return new Tuple<bool, List<string>>(true, errors);
        }

        bool GenerateConfigurationFile()
        {
            try
            {
                var rails30 = CheckRails30();

                var lines = File.ReadAllLines(configFile);

                List<string> outLines = new List<string>();
                foreach (var line in lines)
                {
                    if (line.Contains(@"config/environment"))
                    {
                        // Modify for 'rails new' or save file of NougakuDo.hta
                        outLines.Add(@"require '" + configPath.Replace(@"\", @"/") + @"/config/environment'");
                        //outLines.Add(
                        //    line.Replace(@"::File.expand_path('../config/environment',  __FILE__)",
                        //                 "'" + configPath.Replace(@"\", @"/") +
                        //                 @"/config/environment'"));
                        // No need to fix for unique http.sys's queue name. Because Nougkudo 1.1.7 fixed it.
                        // add 'require' for Rack update
                        if (numProcess != 1)    // Need for Ennoumu. Not need for Ennou.
                        {
                            outLines.Add(@"require 'rack/handler/ennoumu'");
                            // delete # of process condition for for Rack update
                            outLines.Add(@"Rack::Handler::Ennoumu.config :nprocs => " + numProcess.ToString());
                        }
                    }
                    else if (line.Contains("rack/handler/ennou") || (line.Contains("Rack::Handler::Ennoumu.config")) ||
                             line.Contains("require") || (line.Contains("Rack::Handler::Ennou.config")))
                    {
                        // Add "Ennou" condition.
                        // pass for generated configration file.
                    }
                    else
                    {
                        outLines.Add(line);
                    }
                }

                File.WriteAllLines(Path.Combine(configPath, Commons.Constants.ENNOU_CONFIG), outLines);

                var ret = GenerateEnvironmentFile(rails30); // Add for sub directory mode of rails 3.1.
                if (!ret) return ret;
                ret = GenerateApplication(rails30);         // Add for sub directory mode of rails 3.0.
                if (!ret) return ret;
                ret = UpdateProduction();                   // Add for static asset server of Ennou.
                return ret;

                //return true;
            }
            catch (Exception e)
            {
                errors.Add(e.Message);
                return false;
            }
        }

        /// <summary>
        /// Sub directory mode setting for rails 3.1.x, add ENV['RAILS_RELATIVE_URL_ROOT'] to config/environment.rb.
        /// </summary>
        /// <returns></returns>
        bool GenerateEnvironmentFile(bool rails30)
        {
            //if (!ennou110) return true;  // Delete to check NougakuDo 1.1.0 above.
            var environmentFile = Path.Combine(configPath, Commons.Constants.RAILS_ENVIRONMENT);
            try
            {
                var lines = File.ReadAllLines(environmentFile);
                bool update = false;

                List<string> outLines = new List<string>();
                // Move RAILS_RELATIVE_URL_ROOT on top.
                if (!virtualHost && !rails30)
                {
                    outLines.Add(Commons.Constants.RAILS_RELATIVE_URL_ROOT + " = '/" + name + "'");
                    update = true;
                }
                foreach (var line in lines)
                {
                    if (line.ToLower().Contains(Commons.Constants.RAILS_RELATIVE_URL_ROOT.ToLower()))
                    {
                        // delete exsits ENV['RAILS_RELATIVE_URL_ROOT']
                        update = true;
                    }
                    else
                    {
                        outLines.Add(line);
                    }
                }

                //if (!virtualHost)  // Move RAILS_RELATIVE_URL_ROOT on top.
                //    outLines.Add(Commons.Constants.RAILS_RELATIVE_URL_ROOT + " = '/" + name + "'");
                if (update)
                    File.WriteAllLines(environmentFile, outLines);
                return true;
            }
            catch (Exception e)
            {
                errors.Add(e.Message);
                return false;
            }

        }

        /// <summary>
        /// Sub directory mode setting for rails 3.0.x.
        /// </summary>
        /// <param name="rails30"></param>
        /// <returns></returns>
        bool GenerateApplication(bool rails30)
        {
            var applicationFile = Path.Combine(configPath, Commons.Constants.RAILS_APPLICATION);
            try
            {
                var lines = File.ReadAllLines(applicationFile);

                List<string> outLines = new List<string>();
                bool update = false;

                foreach (var line in lines)
                {
                    if (line.ToLower().Contains(Commons.Constants.RAILS_ASSET_PATH.ToLower()))
                    {
                        // delete exsits config.action_controller.asset_path
                        update = true;
                    }
                    else if (line.Contains(Commons.Constants.RAILS_ASSET_PATH_POINT))
                    {
                        outLines.Add(line);
                        if (rails30)
                        {
                            outLines.Add("    " + Commons.Constants.RAILS_ASSET_PATH + " = '/" + name + "%s'");
                            update = true;
                        }
                    }
                    else
                    {
                        outLines.Add(line);
                    }
                }

                if (update)
                    File.WriteAllLines(applicationFile, outLines);
                return true;
            }
            catch (Exception e)
            {
                errors.Add(e.Message);
                return false;
            }
        }

        /// <summary>
        /// Static asset server setting for Ennou.
        /// </summary>
        /// <returns></returns>
        bool UpdateProduction()
        {
            var productionFile = Path.Combine(configPath, Commons.Constants.RAILS_PRODUCTION);
            try
            {
                var lines = File.ReadAllLines(productionFile);
                bool update = false;

                List<string> outLines = new List<string>();

                foreach (var line in lines)
                {
                    if (line.Contains(Commons.Constants.RAILS_STATIC_ASSETS))
                    {
                        if (line.Contains("false"))
                        {
                            update = true;
                            outLines.Add(line.Replace("false", "true"));
                        }
                        else
                            outLines.Add(line);
                    }
                    else
                    {
                        outLines.Add(line);
                    }
                }

                if (update)
                    File.WriteAllLines(productionFile, outLines);
                return true;
            }
            catch (Exception e)
            {
                errors.Add(e.Message);
                return false;
            }
        }

        /// <summary>
        /// Check rails 3.0.x. other is rails 3.1.x.
        /// </summary>
        /// <returns>true:rails 3.0, false:rails 3.1</returns>
        bool CheckRails30()
        {
            var gemfile = Path.Combine(configPath, Commons.Constants.RAILS_GEMFILE);
            try
            {
                var lines = File.ReadAllLines(gemfile);
                foreach (var line in lines)
                {
                    if (line.Contains("gem 'rails'"))
                    {
                        if (line.Contains("'3.0"))
                            return true;
                        else
                            return false;
                    }
                }
            }
            catch (Exception e)
            {
                errors.Add(e.Message);
            }
            return false;
        }

        bool GenerateCommandFile()
        {
            try
            {
                List<string> cmds = new List<string>();
                cmds.Add("\"" + rubyExecutable + "\"");                               // Add double quotation.
                cmds.Add(GenerateCommandArgs());
                cmds.Add(Commons.Constants.RUBY_BUNDLE_ENVIRONMENT_VARIABLE + "=" +
                         Path.Combine(configPath, Commons.Constants.RUBY_GEMFILE));     // Add BUNDLE_GEMFILE= Gemfile path for Environment variable.

                File.WriteAllLines(Path.Combine(configPath, Commons.Constants.ENNOU_EXECUTABLE), cmds);

                return true;
            }
            catch (Exception e)
            {
                errors.Add(e.Message);
                return false;
            }
        }

        string GenerateCommandArgs()
        {
            // Change command line arge
            //     from  -C[Ruby full path]\bin rackup -p 80 -E production -s Ennou [config.ru full path]\configEnnou.ru
            //     to    -C[Ruby full path]\bin [full path]\bundle exec rackup -p 80 -E production -s Ennou [config.ru full path]\configEnnou.ru
            string args = "-C" + rubyPath + " " +                           // Path of command
                            Path.Combine(rubyPath, "bundle") + " exec " +   // Bundle script
                            //"rackup ";                                      // Rack script
                            Path.Combine(rubyPath, "rackup") + " ";         // Rack script
            if (debugging)

            if (debugging)
                args += "-d ";                                          // debbuging flags
            if (warnings)
                args += "-w ";                                          // warnings flags

            if (virtualHost)                                            // Add for NougakuDo 1.1.0 above.
                args += "-o " + hostName + " ";

            args += "-p 80 " +                                          // loop and commandline
                    "-E " + environment + " ";                          // production、development、test

            // setting -s option (single process or multi process)
            if (numProcess == 1)
            {
                args += "-s Ennou ";                                    // 1 of process commnad
            }
            else
            {
                args += "-s Ennoumu ";                                  // num of process command
            }
            args += Path.Combine(configPath, Commons.Constants.ENNOU_CONFIG);
            return args;
        }


    }
}
